/*Copyright (c) 2009 Michael Streif
 *
 *Permission is hereby granted, free of charge, to any person obtaining a copy
 *of this software and associated documentation files (the "Software"), to deal
 *in the Software without restriction, including without limitation the rights
 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *copies of the Software, and to permit persons to whom the Software is
 *furnished to do so, subject to the following conditions:
 *
 *The above copyright notice and this permission notice shall be included in
 *all copies or substantial portions of the Software.
 *
 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *THE SOFTWARE.
 */

#include "physmm.h"
#include "vmm.h"
#include "printk.h"

// TODO: Code function to allocate a range of stack frames! (needs a buffer and
//       number of page frames to allocate as arguments)


#define END_OF_STACK 0xE5D0FBADB01
#define START_OF_STACK 0xBE60FBADB01

#define PAGE_ALIGN(addr) (4096 + ((addr - 1) & ~ (4096 - 1)))

extern uint64 kernelEnd;

uint64* pageStack = &kernelEnd;

uint64* AllocPageFrame()
{
	pageStack--;

	if (*pageStack == END_OF_STACK)
	{
		pageStack++;
		return 0;
	}
	else
	{
		return (uint64*) *pageStack;
	}


}

void FreePageFrame(uint64* addr)
{
	if (*pageStack == START_OF_STACK)
	{
		// that's not possible...free was called too often!
		return;
	}
	else
	{
		*pageStack = (uint64) addr;
		pageStack++;
	}
}


uint64 mappedin = 0;

// NOTE: in order for this to work, there has at least to be 16 bytes of mapped memory after the kernel!!!
// probably should ensure that during startup...
void SetupPhysMM(multibootInfo* mbInfo)
{

	/* *pageStack++ = END_OF_STACK;
	*pageStack++ = 0x000200000;
	*pageStack++ = 0x000201000;
	*pageStack++ = 0x000202000;
	*pageStack++ = 0x000203000;
	*pageStack++ = 0x000204000;
	*pageStack++ = 0x000205000;
	*pageStack++ = 0x000206000;
	*pageStack = START_OF_STACK;*/


	uint64 kEnd = PAGE_ALIGN((uint64) &kernelEnd) - 0xFFFF800000000000;
	memoryMap* mmap = (memoryMap*) (uint64) mbInfo->mmapAddr;
	uint64 mmapEnd = mbInfo->mmapAddr + mbInfo->mmapLength;

	// at higher half + 2MB!
	uint64 endOfMappedMem = 0xFFFF800000200000;

	*pageStack++ = END_OF_STACK;

	// note: there have to be at least 5 uint46 mapped in after the kernel!!!
	while ((uint64) mmap < mmapEnd)
	{
		if ((mmap->baseAddr >= 0x100000) && (mmap->type == 1))
		{
			uint64 addr = PAGE_ALIGN(mmap->baseAddr);

			while (addr < mmap->baseAddr + mmap->length)
			{
				if (addr >= kEnd)
				{
					if ((uint64) pageStack >= endOfMappedMem)
					{
						// *pageStack = START_OF_STACK;
						//return;

						mapPage((uint64*) endOfMappedMem, KERNEL_PAGE);
						mappedin++;
						endOfMappedMem += 4096;
					}

					*pageStack++ = addr;
				}

				addr += 4096;
			}
		}

		mmap = (memoryMap*) ((uint64) mmap + mmap->size + sizeof(int32));
	}

	*pageStack = START_OF_STACK;
}

void printGrubMemMap(multibootInfo* mbInfo)
{
	memoryMap* mmap = (memoryMap*) (uint64) mbInfo->mmapAddr;
	uint64 mmapEnd = mbInfo->mmapAddr + mbInfo->mmapLength;

	while ((uint64) mmap < mmapEnd)
	{
		printk("start: %h \n", mmap->baseAddr);
		printk("length: %h \n", mmap->length);
		printk("type: %d \n\n", mmap->type);

		mmap = (memoryMap*) ((uint64) mmap + mmap->size + sizeof(int32));
	}
}

void printPgFrameStack()
{
	uint64* ptr = pageStack;
	uint64 i = 0;

	while (*ptr != END_OF_STACK)
	{
		printk("%h ", *ptr);
		ptr--;
		i++;
	}

	printk("\n%d\n", i);
	printk("mapped in: %d\n", mappedin);
}
